package com.sh.d1.gatewayserver;

import com.sh.d1.gameserver.async.AsyncOperationProcessor;
import com.sh.d1.gameserver.msg.GameMsgProtocol;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
import com.sh.util.DLock;
import com.sh.util.RedisUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.Jedis;

public class CheckDuplicateLoginHander extends ChannelInboundHandlerAdapter {

    static private final Logger LOGGER = LoggerFactory.getLogger(CheckDuplicateLoginHander.class);

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msgObj) throws Exception {
        if (!(msgObj instanceof BinaryWebSocketFrame)) {
            return;
        }

        BinaryWebSocketFrame inputFrame = (BinaryWebSocketFrame) msgObj;
        ByteBuf oldByteBuf = inputFrame.content();

        // 读掉消息长度
        oldByteBuf.readShort();

        // 读取网关服务器 Id
        int gatewayServerId = oldByteBuf.readInt();
        // 读取会话 Id
        int sessionId = oldByteBuf.readInt();
        // 读取用户 Id
        int fromUserId = oldByteBuf.readInt();
        //消息编码
        int msgCode = oldByteBuf.readShort();
        //重置读取索引
        oldByteBuf.resetReaderIndex();


        if (GameMsgProtocol.MsgCode.USER_LOGIN_RESULT_VALUE == msgCode) {
            checkDuplicateLogin(ctx, msgObj, sessionId, fromUserId);
        }else{
            ctx.fireChannelRead(msgObj);
        }
    }

    /**
     * 检查重复登录
     *
     * @param ctx        信道处理器上下文
     * @param sessionId  会话id
     * @param fromUserId 用户id
     */
    private void checkDuplicateLogin(ChannelHandlerContext ctx, Object msObj, int sessionId, int fromUserId) {
        if (null == ctx ||
                fromUserId <= 0) {
            return;
        }
        //获取客户端信道
        Channel clientCh = ClientChannelGroup.getBySessionId(sessionId);
        if (null == clientCh) {
            return;
        }

        //获取已经登录的信道
        Channel existsCh = ClientChannelGroup.getByUserId(fromUserId);
        if (null != existsCh) {
            try {
                LOGGER.info("断开已有连接,userId={}",
                        fromUserId);
                //断开已经登录用户
                existsCh.disconnect().sync();
                //断开最新登录用户
                clientCh.disconnect().sync();
                return;
            } catch (InterruptedException ex) {
                LOGGER.error(ex.getMessage(), ex);
            }

        }
        LOGGER.info("执行线程 = {}", Thread.currentThread().getName());

        AsyncOperationProcessor.getInstance().process(fromUserId,
                //执行异步操作
                () -> {


                    try(DLock dLock = DLock.trylockAndGet("UserLogin_" + fromUserId,2000);) {
                        if (null == dLock) {
                            LOGGER.warn("分布式锁加锁失败！userId={}", fromUserId);
                            clientCh.disconnect().sync();
                            return;
                        }
                        if (checkDuplicateLoginRedis(fromUserId)) {
                            LOGGER.info("绑定用户Id,sessionId = {},fromUserId = {}",
                                    sessionId,
                                    fromUserId);
                            IdSetterGetter.setUserId(clientCh, fromUserId);
                        } else {
                            LOGGER.warn("发生重复登录 断开连接！ userId={}", fromUserId);
                            clientCh.disconnect().sync();
                        }

                    } catch (Exception ex) {
                        LOGGER.error(ex.getMessage(), ex);
                    }
                },
                ctx.executor(),

                () -> {
                    if (clientCh.isOpen()) {
                        ctx.fireChannelRead(msObj);
                    }
                });

    }

    /**
     * 通过redis检查是否有重复登陆
     *
     * @param userId
     * @return true=检查成功  没有重复登陆 false=检查结果失败，有重复登陆
     */
    private boolean checkDuplicateLoginRedis(int userId) {
        if (userId <= 0) {
            return false;
        }
        try (Jedis redis = RedisUtil.getRedis()) {
            String key_userId = "User_" + userId;
            String field_atGatewayServerId = "AtGatewayServerId";
            String key_gatewayServerId = "GatewayServer_" + GatewayServer.getId();

            String strAtGatewayServerId = redis.hget(key_userId, field_atGatewayServerId);
            if (null == strAtGatewayServerId) {
                Long result = redis.hsetnx(key_userId, field_atGatewayServerId, GatewayServer.getId());
                if (null == result || result.intValue() <= 0) {
                    return false;
                }
                redis.hset(key_gatewayServerId, key_userId, "1");
                return true;
            }
            if (strAtGatewayServerId.equals(GatewayServer.getId())) {
                return true;
            }
            boolean isExit = redis.hexists("GatewayServer_" + strAtGatewayServerId, key_userId);
            if (isExit) {
                redis.publish(
                        RedisSusbcriber.CHANNEL_DISCONNECT_DUPLICATE_LOGIN,
                        String.valueOf(userId)
                );

                return false;
            }
            //目标空更新数据
            redis.hset(key_userId, field_atGatewayServerId, GatewayServer.getId());
            redis.hset(key_gatewayServerId, key_userId, String.valueOf(userId));
            return true;
        } catch (Exception ex) {
            LOGGER.error(ex.getMessage(), ex);
        }
        return false;
    }
}
